#ifndef __COMPONENTS_MANAGER_H__
#define __COMPONENTS_MANAGER_H__

#include <cstdint>
#include <unordered_map>
#include <vector>
#include <string>
#include <map>
#include <mutex>
#include <exception>

#include "spdlog/fmt/fmt.h"
#include "nlohmann/json.hpp"
#include "entt/entt.hpp"

#include "Game/TableComponent.hpp"

#include "SimpleSingleton.hpp"
#include "Log.h"
#include "Utils/Hash.hpp"

namespace PocketSurvival
{

    struct Component;

    struct BaseComponentData
    {
        entt::id_type hashValue;
        std::function<Component*()> createFunction;
    };


    class ComponentsManager : public SimpleSingletonItem
    {
        friend class SimpleSingleton;
    private:
        std::unordered_map<std::string, BaseComponentData> m_baseComponentMap;
        std::mutex m_mapMutex;

    private:
        ComponentsManager();
        ~ComponentsManager();
        ComponentsManager(const ComponentsManager &) = delete;
        ComponentsManager(ComponentsManager &&) = delete;
        ComponentsManager &operator=(const ComponentsManager &) = delete;
        ComponentsManager &operator=(ComponentsManager &&) = delete;

    public:

        void update(float dt);

        void loadGame();

        /***********************************************************************************************************
         * @brief 移除实体的所有组件
         * @param entity 实体对象
        ***********************************************************************************************************/
        void entityRemoveComponents(entt::entity entity);



        /***********************************************************************************************************
         * @brief 给实体添加对应的组件
         * @param entity 实体对象
         * @param jsonData 组件数据
        ***********************************************************************************************************/
        void entityAddComponents(entt::entity entity ,const nlohmann::json &jsonData);



        /***********************************************************************************************************
         * @brief 保存实体内的组件数据
         * @param entity 需要保存数据的实体
         * @param jsonData 用于存放组件数据的对象
        ***********************************************************************************************************/
        void saveComponents(entt::entity entity, nlohmann::json &jsonData);



        /***********************************************************************************************************
         * @brief 保存实体的数据，该数据是客户端可以识别并能够使用到的，跟服务器数据的保存形式是不同的
         * @param entity 需要保存数据的实体
         * @param jsonData 用于存放组件数据的对象
        ***********************************************************************************************************/
        void saveEntityToMsgJson(entt::entity entity, nlohmann::json &jsonData);


        template<typename T>
        inline void emplaceComponent()
        {
            m_mapMutex.lock();

            BaseComponentData baseData;
            baseData.hashValue = entt::type_id<T*>().hash();
            baseData.createFunction = []() -> Component*
            {
                return static_cast<Component*>(new T);
            };

            if (m_baseComponentMap.find(T::GetName()) != m_baseComponentMap.end())
            {
                throw std::runtime_error(fmt::format("不允许重复注册组件：{}",T::GetName()));
            }
            m_baseComponentMap[T::GetName()] = baseData;

            m_mapMutex.unlock();
        }


        template<typename T, typename T2>
        inline void replcaeOrEmplaceComponent()
        {
            m_mapMutex.lock();

            BaseComponentData baseData;
            baseData.hashValue = entt::type_id<T*>().hash();
            baseData.createFunction = []() -> Component*
            {
                return static_cast<Component*>(new T2);
            };
            m_baseComponentMap[T::GetName()] = baseData;

            Log::Info(fmt::format("替换组件 {} -> {}", Utils::Hash::ToTypeName<T>(), Utils::Hash::ToTypeName<T2>()));

            m_mapMutex.unlock();
        }


        template<typename T>
        inline void deleteComponent()
        {
            m_mapMutex.lock();
            auto it = m_baseComponentMap.find(T::GetName());
            if(it != m_baseComponentMap.end())
            {
                delete it->second.baseObj;
                m_baseComponentMap.erase(it);
            }
            m_mapMutex.unlock();
        }
    };


} // namespace PocketSurvival


#endif

